package com.shengqin.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.hash.Hash;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shengqin.common.pojo.CommonPage;
import com.shengqin.common.pojo.CommonResult;
import com.shengqin.config.CommonException;
import com.shengqin.config.RedisKeys;
import com.shengqin.entity.*;
import com.shengqin.listeners.SchoolListener;
import com.shengqin.listeners.StudentDataListener;
import com.shengqin.mapper.StudentMapper;
import com.shengqin.pojo.grade.GradeModel;
import com.shengqin.pojo.school.*;
import com.shengqin.pojo.student.StudentImportParam;
import com.shengqin.service.*;
import com.shengqin.service.impl.UserRelSchoolService;
import com.shengqin.utils.GradeUtil;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 学校 前端控制器
 * </p>
 *
 * @author Rui.Zhou
 * @since 2023-12-05
 */
@Slf4j
@RestController
@RequestMapping("admin/v1/school")
public class SchoolController {

    @Resource
    private ISchoolService schoolService;
    @Resource
    private IClassesService classesService;
    @Resource
    private IStudentService studentService;
    @Resource
    private IUserService userService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private IPlanRelationClassesService planRelationClassesService;
    @Resource
    private StudentMapper studentMapper;
    @Resource
    private UserRelSchoolService userRelSchoolService;


    @Operation(summary = "导入学校")
    @PostMapping("import")
    public CommonResult importSchool(@RequestPart("file") MultipartFile file) {
        try {
            Set<String> errors = new HashSet<>();
            EasyExcel.read(file.getInputStream(), SchoolImportParam.class, new SchoolListener(schoolService, errors)).sheet().doRead();
        } catch (ExcelDataConvertException e) {
            String message = String.format("第%s行，第%s列解析异常，请修改后重新上传", e.getRowIndex() + 1, e.getColumnIndex() + 1);
            throw new CommonException(message);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        }
        return CommonResult.ok().setMsg("导入成功");
    }

    @PostMapping("summaryData")
    public CommonResult<Map<String, Object>> summaryData(@RequestBody SchoolIdParam schoolIdParam) {
        Map<String, Object> resultMap = new HashMap<>();
        if (schoolIdParam.getSchoolId() == null) {
            return CommonResult.data(new HashMap<>());
        }
        School school = schoolService.getById(schoolIdParam.getSchoolId());
        List<Classes> classesList = classesService.listBySchoolId(school.getId());
        List<Student> studentList = studentService.listBySchoolId(school.getId());
        
        // 使用Stream API统计男女学生数量
        Map<String, Long> genderCounts = studentList.stream()
            .filter(student -> student.getSex() != null)
            .collect(Collectors.groupingBy(
                Student::getSex,
                Collectors.counting()
            ));

        long manCount = genderCounts.getOrDefault("1", 0L);
        long womanCount = genderCounts.getOrDefault("2", 0L);

        resultMap.put("school", school);
        resultMap.put("classesCount", classesList.size());
        resultMap.put("studentCount", studentList.size());
        resultMap.put("manCount", manCount);
        resultMap.put("womanCount", womanCount);
        return CommonResult.data(resultMap);
    }

    @PostMapping("queryList")
    public CommonResult<List<School>> getSchoolList() {
        if (!StpUtil.getLoginId().equals("1")) {
            User user = userService.getById(StpUtil.getLoginId().toString());
            List<School> schools = schoolService.listByUserId(user.getId());
            return CommonResult.data(schools);
        }
        return CommonResult.data(schoolService.list());
    }

    @PostMapping("getGradeList")
    public CommonResult<List<GradeModel>> getGradeList(@RequestBody SchoolIdParam schoolIdParam) {
        School school = schoolService.getById(schoolIdParam.getSchoolId());
        if(school == null || !StringUtils.hasLength(school.getSchoolType())){
            return CommonResult.data(new ArrayList<>());
        }
        List<GradeModel> gradeEnumList = GradeUtil.getGradeList(school.getSchoolType());
        return CommonResult.data(gradeEnumList);
    }

    @PostMapping("getSchoolGradeClassesList")
    public CommonResult<List<TreeModel>> getSchoolGradeClassesList(@RequestBody SchoolQueryParam queryParam) {
        // 1. 获取学校列表
        QueryWrapper<School> schoolQueryWrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(queryParam.getName())) {
            schoolQueryWrapper.lambda().like(School::getName, queryParam.getName());
        }
        String loginId = StpUtil.getLoginId().toString();
        User user = userService.getById(loginId);
        List<School> schoolList = new ArrayList<>();
        if (user != null) {
            schoolList = schoolService.listByUserId(user.getId());
        }
        if (CollectionUtils.isEmpty(schoolList)) {
            return CommonResult.data(new ArrayList<>());
        }

        // 2. 并发获取班级和学生数据
        List<Integer> schoolIds = schoolList.stream()
                .map(School::getId)
                .collect(Collectors.toList());

        CompletableFuture<Map<Integer, List<Classes>>> classesFuture = CompletableFuture
                .supplyAsync(() -> {
                    List<Classes> classesList = classesService.listBySchoolIds(schoolIds);
                    return classesList.stream()
                            .filter(c -> StringUtils.hasLength(c.getCode()))
                            .collect(Collectors.groupingBy(Classes::getSchoolId));
                });

        CompletableFuture<Map<Integer, Map<Integer, Long>>> studentCountsFuture = CompletableFuture
                .supplyAsync(() -> {
                    // 使用SQL直接统计学生数量
                    List<Map<String, Object>> countResults = studentMapper.countStudentsBySchoolAndClass(schoolIds);
                    
                    return countResults.stream()
                            .collect(Collectors.groupingBy(
                                result -> Integer.valueOf(String.valueOf(result.get("school_id"))),
                                Collectors.groupingBy(
                                    result -> Integer.valueOf(String.valueOf(result.get("classes_id"))),
                                    Collectors.mapping(
                                        result -> Long.valueOf(String.valueOf(result.get("student_count"))),
                                        Collectors.reducing(0L, (a, b) -> b)
                                    )
                                )
                            ));
                });

        // 3. 等待所有异步操作完成
        Map<Integer, List<Classes>> schoolClassesMap;
        Map<Integer, Map<Integer, Long>> studentCountsMap;
        try {
            schoolClassesMap = classesFuture.get(10, TimeUnit.SECONDS);
            studentCountsMap = studentCountsFuture.get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("获取班级或学生数据时发生错误", e);
            return CommonResult.data(new ArrayList<>());
        }

        // 4. 构建树形结构
        List<TreeModel> treeModelList = schoolList.parallelStream().map(school -> {
            TreeModel treeModel = new TreeModel();
            treeModel.setId("school_" + school.getId());
            treeModel.setType("school");

            List<GradeModel> gradeEnumList = GradeUtil.getGradeList(school.getSchoolType());
            List<Classes> schoolClassesList = schoolClassesMap.getOrDefault(school.getId(), new ArrayList<>());
            Map<Integer, List<Classes>> gradeClassesMap = schoolClassesList.stream()
                    .collect(Collectors.groupingBy(Classes::getGradeId));

            List<TreeModel> gradeList = gradeEnumList.stream().map(gradeModel -> {
                TreeModel gradeTreeModel = new TreeModel();
                gradeTreeModel.setType("grade");
                gradeTreeModel.setId(school.getId() + "_grade_" + gradeModel.getId());

                List<Classes> gradeClasses = gradeClassesMap.getOrDefault(gradeModel.getId(), new ArrayList<>());
                gradeClasses.sort(Comparator.comparing(Classes::getCode));

                List<TreeModel> classesTreeList = gradeClasses.stream().map(classes -> {
                    TreeModel classesTreeModel = new TreeModel();
                    classesTreeModel.setType("classes");
                    classesTreeModel.setId("classes_" + classes.getId());
                    
                    // 获取班级学生数量
                    long studentCount = studentCountsMap
                        .getOrDefault(school.getId(), Collections.emptyMap())
                        .getOrDefault(classes.getId(), 0L);
                    
                    classesTreeModel.setName(classes.getName() + "（学生数：" + studentCount + "）");
                    return classesTreeModel;
                }).collect(Collectors.toList());

                gradeTreeModel.setChildren(classesTreeList);
                gradeTreeModel.setCount(classesTreeList.size());
                gradeTreeModel.setName(gradeModel.getName() + "（班级数：" + classesTreeList.size() + "）");
                return gradeTreeModel;
            }).collect(Collectors.toList());

            treeModel.setChildren(gradeList);
            treeModel.setCount(gradeList.size());
            treeModel.setName(school.getName() + "（年级数：" + gradeList.size() + "）");
            return treeModel;
        }).collect(Collectors.toList());

        return CommonResult.data(treeModelList);
    }

    @PostMapping("getGradeClassesList")
    public CommonResult<List<GradeModel>> getGradeClassesList(@RequestBody SchoolIdParam schoolIdParam) {
        School school = schoolService.getById(schoolIdParam.getSchoolId());
        List<Classes> classesList = classesService.listBySchoolId(school.getId());
        Map<Integer, List<Classes>> gradeClassesMap = classesList.stream().collect(Collectors.groupingBy(Classes::getGradeId));
        List<GradeModel> gradeEnumList = GradeUtil.getGradeList(school.getSchoolType());
        gradeEnumList.forEach(gradeModel -> {
            gradeModel.setClassesList(gradeClassesMap.getOrDefault(gradeModel.getId(), new ArrayList<>()));
        });
        return CommonResult.data(gradeEnumList);
    }

    @PostMapping("save")
    public CommonResult<Boolean> onSave(@RequestBody School school) {
        if (school.getId() == null) {
            School schoolByName = schoolService.getByNameAndLoginId(school.getName().trim());
            if (schoolByName != null) {
                throw new CommonException("学校名称已存在，无法添加！");
            }
            school.setCreateTime(LocalDateTime.now());
            school.setCreateUserId(Integer.valueOf(StpUtil.getLoginId().toString()));
        } else {
            School oldSchool = schoolService.getById(school.getId());
            if (!oldSchool.getName().equals(school.getName())) {
                School schoolByName = schoolService.getByNameAndLoginId(school.getName().trim());
                if (schoolByName != null) {
                    throw new CommonException("学校名称已存在，无法添加！");
                }
            }
        }
        boolean isOk = schoolService.saveOrUpdate(school);

        Set<Long> userIdList = new HashSet<>();
        userIdList.add(1L);
        userIdList.add(Long.valueOf(StpUtil.getLoginId().toString()));
        List<User> users = userService.listByIds(userIdList);
        if (!CollectionUtils.isEmpty(users)) {
            userRelSchoolService.removeByUserIdsAndSchoolId(userIdList, school.getId());
            users.forEach(user -> {
                UserRelSchool userRelSchool = new UserRelSchool();
                userRelSchool.setUserId(user.getId());
                userRelSchool.setSchoolId(school.getId());
                userRelSchool.setSchoolName(school.getName());
                userRelSchoolService.save(userRelSchool);
            });
            userRelSchoolService.updateSchoolName(school.getId(),school.getName());
        }

        if (!CollectionUtils.isEmpty(school.getSchoolTypes())) {
            school.setSchoolType(String.join(",", school.getSchoolTypes()));
        }
        return CommonResult.data(isOk);
    }

    @PostMapping("page")
    public CommonResult<Page<School>> getPage(@RequestBody CommonPage<School> pageParam) {
        if (!StpUtil.getLoginId().toString().equals("1")) {
            User user = userService.getById(StpUtil.getLoginId().toString());
            List<School> schools = schoolService.listByUserId(user.getId());
            if (CollectionUtils.isEmpty(schools)) {
                return CommonResult.data(new Page<>());
            }
            pageParam.getQueryParam().setSchoolIds(schools.stream().map(s -> s.getId().toString()).collect(Collectors.toSet()));
        }
        return CommonResult.data(schoolService.getPage(pageParam));
    }

    @PostMapping("delete")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> delete(@RequestBody School school) {

        List<String> schoolIds = new ArrayList<>();
        schoolIds.add(school.getId().toString());
        List<PlanRelationClasses> schoolRelationList = planRelationClassesService.listBySchoolIds(schoolIds);
        if (!CollectionUtils.isEmpty(schoolRelationList)) {
            throw new CommonException("该学校已绑定计划，无法删除");
        }

        School oldSchool = schoolService.getById(school.getId());
        userRelSchoolService.removeBySchoolId(school.getId());
        classesService.removeBySchoolId(school.getId());
        studentService.removeBySchoolId(school.getId());
        redisTemplate.opsForHash().delete(RedisKeys.SCHOOL_MAP, oldSchool.getName().trim());

        return CommonResult.data(schoolService.removeById(school.getId()));
    }

    @PostMapping("clearStudent")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> clearStudent(@RequestBody School school) {

        User u = userService.getById(StpUtil.getLoginId().toString());
        String password = MD5.create().digestHex(school.getPassword());
        if (!u.getPassword().equals(password)) {
            throw new CommonException("账号密码错误");
        }

        classesService.removeBySchoolId(school.getId());
        studentService.removeBySchoolId(school.getId());

        return CommonResult.data(true);
    }

    @PostMapping("batchClearStudent")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> batchClearStudent(@RequestBody SchoolClearParam schoolClearParam) {

        User u = userService.getById(StpUtil.getLoginId().toString());
        String password = MD5.create().digestHex(schoolClearParam.getPassword());
        if (!u.getPassword().equals(password)) {
            throw new CommonException("账号密码错误");
        }

        if (!CollectionUtils.isEmpty(schoolClearParam.getSchoolIds())) {
            schoolClearParam.getSchoolIds().forEach(schoolId -> {
                classesService.removeBySchoolId(Integer.valueOf(schoolId));
                studentService.removeBySchoolId(Integer.valueOf(schoolId));
            });
        }

        return CommonResult.data(true);
    }

}
